﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.XPath;
using System.Xml;

namespace ESBConfigUtility
{
    class Program
    {
        const string LOG_FILE_NAME = @"\ESB.Portal.Configuration.Log.txt";
        const string REG_EsbToolkitInstallPath_FOLDER = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\BizTalk ESB Toolkit";
        const string REG_EsbToolkitInstallPath_KEY = @"InstallPath";

        const string XPATH_Resolvers = @"//resolvers";
        const string XPATH_AdapterProviders = @"//adapterProviders";
        const string XPATH_Filters = @"//filters";
        const string XPATH_ItineraryServices = @"//itineraryServices";
        const string XPATH_ItineraryOMs = @"//itineraryOM";

        static string esbConfigFullPath = string.Empty;
        static XmlDocument esbConfigXml = null;
        
        static void Main(string[] args)
        {
            try
            {
                string command = args[0].ToUpper();
                if (command != "SET-RESOLVER" && command != "SET-ADAPTERPROVIDER" && command != "SET-ITINERARYSERVICE" && command != "SET-FILTER" && command != "SET-ITINERARYOM")
                {
                    PrintUsage();
                    return;
                }

                esbConfigFullPath = GetEsbConfigPath() + @"\esb.config";
                esbConfigXml = new XmlDocument();
                esbConfigXml.Load(esbConfigFullPath);
                WriteOutput("Loaded esb.config");

                var parameters = GetParamters(args);

                String msg = String.Empty;
                switch (command)
                {
                    case ("SET-RESOLVER"):
                        {
                            if (args.Length == 3)
                            {
                                msg = SetResolver(parameters["NAME"], parameters["TYPE"], null);
                            }
                            else if (args.Length == 4)
                            {
                                msg = SetResolver(parameters["NAME"], parameters["TYPE"], parameters["CONFIG"]);
                            }
                            else
                            {
                                PrintUsage();
                            }
                            break;
                        }
                    case ("SET-ADAPTERPROVIDER"):
                        {
                            if (args.Length == 4)
                            {
                                msg = SetAdapterProvider(parameters["NAME"], parameters["TYPE"], parameters["MONIKER"]);
                            }
                            else
                            {
                                PrintUsage();
                            }
                            break;
                        }
                    case ("SET-ITINERARYSERVICE"):
                        {
                            if (args.Length == 6)
                            {
                                msg = SetItineraryService(parameters["ID"], parameters["NAME"], parameters["TYPE"], args[4], args[5], null);
                            }
                            else if (args.Length == 7)
                            {
                                msg = SetItineraryService(parameters["ID"], parameters["NAME"], parameters["TYPE"], args[4], args[5], args[6]);
                            }
                            else
                            {
                                PrintUsage();
                            }
                            break;
                        }
                    case ("SET-FILTER"):
                        {
                            if (args.Length == 3)
                            {
                                msg = SetFilter(args[1], args[2]);
                            }
                            else
                            {
                                PrintUsage();
                            }
                            break;
                        }
                    case ("SET-ITINERARYOM"):
                        {
                            if (args.Length == 4)
                            {
                                msg = SetItineraryOM(args[1], args[2], args[3]);
                            }
                            else
                            {
                                PrintUsage();
                            }
                            break;
                        }
                }
                WriteOutput(msg);
            }
            catch (Exception e)
            {
                WriteOutput("ERROR: " + e.ToString());
            }
        }

        static private void PrintUsage()
        {
            Console.WriteLine("Usage:");
            Console.WriteLine();
            Console.WriteLine("ESBConfigUtility set-Resolver -Name:<ResolverName> -Type:<Fully Qualified Assembly Type> [-Config:<Resolver XML Configuration>]");
            Console.WriteLine("ESBConfigUtility set-AdapterProvider -Name:<Adapter Provider Name> -Type:<Fully Qualified Assembly Type> -Moniker:<Moniker>");
            Console.WriteLine("ESBConfigUtility set-ItineraryService -Id:<Guid> -Name:<Itinerary Service Name> -Type:<Fully Qualified Assembly Type> -Scope:<Messaging|Orchestration> -Stage:<All|AllReceive|AllSend|None> [-ServiceCategory:<Broker>]");
            Console.WriteLine("ESBConfigUtility set-Filter -Name:<FilterName> -Type:<Fully Qualified Assembly Type>");
            Console.WriteLine("ESBConfigUtility set-ItineraryOM -Name:<Itinerary> -Namespace:<Namespace> -Type:<Fully Qualified Assembly Type>");
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Example: ESBConfigUtility set-Resolver -Name:STATIC -Type:\"Microsoft.Practices.ESB.Resolver.STATIC.ResolveProvider, Microsoft.Practices.ESB.Resolver.STATIC, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" -Config:<resolverConfig><add name=\"MyProperty\" value=\"123\" /></resolverConfig>");
            Console.WriteLine("Example: ESBConfigUtility set-AdapterProvider -Name:FILE -Type:\"Microsoft.Practices.ESB.Adapter.FILE.AdapterProvider, Microsoft.Practices.ESB.Adapter.FILE, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" -Moniker:File");
            Console.WriteLine("Example: ESBConfigUtility set-ItineraryService -Id:6a594d80-91f7-4e10-a203-b3c999b0f55e -Name:Microsoft.Practices.ESB.Services.Routing -Type:\"Microsoft.Practices.ESB.Itinerary.Services.RoutingService, Microsoft.Practices.ESB.Itinerary.Services, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\" -Scope:Messaging -Stage:AllReceive");
            Console.WriteLine("Example: ESBConfigUtility set-Filter -Name:XPATH -Type:\"Microsoft.Practices.ESB.Filters.XPath.XPathFilter, Microsoft.Practices.ESB.Filters.XPath, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\"");
            Console.WriteLine("Example: ESBConfigUtility set-ItineraryOM -Name:Itinerary -Namespace:http://schemas.microsoft.biztalk.practices.esb.com/itinerary -Type:\"Microsoft.Practices.ESB.Itinerary.OM.V1.ItineraryV1, Microsoft.Practices.ESB.Itinerary.OM.V1, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35\"");
            Console.WriteLine();
        }

        #region Utility Functions

        static private string SetResolver(string Name, string Type, string Config)
        {
            WriteOutput(string.Format("Setting Resolver with Name = {0}, Type = {1}, Config of {2}...", Name, Type, Config));
            
            var configParentNode = esbConfigXml.SelectSingleNode(XPATH_Resolvers);
            if (configParentNode != null)
            {
                WriteOutput("RESOLVERS = " + configParentNode.OuterXml);
                var existingConfigNode = configParentNode.SelectSingleNode(@"./resolver[@name='" + Name + "']");

                if (existingConfigNode == null)
                {
                    var newConfigNode = configParentNode.FirstChild.Clone();
                    newConfigNode.Attributes["name"].Value = Name;
                    newConfigNode.Attributes["type"].Value = Type;
                    newConfigNode.InnerXml = Config;
                    configParentNode.AppendChild(newConfigNode);
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Resolver Added to ESB Configuration!";
                }
                else
                {
                    existingConfigNode.Attributes["name"].Value = Name;
                    existingConfigNode.Attributes["type"].Value = Type;
                    existingConfigNode.InnerXml = Config;
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Resolver Updated in ESB Configuration!";
                }
            }
            else
            {
                return "Resolvers Xpath query returned null...ending process...";
            }
        }

        static private string SetAdapterProvider(string Name, string Type, string Moniker)
        {
            WriteOutput(string.Format("Setting Adapter Provider with Name = {0}, Type = {1}, Moniker = {2}...", Name, Type, Moniker));

            var configParentNode = esbConfigXml.SelectSingleNode(XPATH_AdapterProviders);
            if (configParentNode != null)
            {
                WriteOutput("ADAPTER PROVIDERS = " + configParentNode.OuterXml);
                var existingConfigNode = configParentNode.SelectSingleNode(@"./adapterProvider[@name='" + Name + "']");

                if (existingConfigNode == null)
                {
                    var newConfigNode = configParentNode.FirstChild.Clone();
                    newConfigNode.Attributes["name"].Value = Name;
                    newConfigNode.Attributes["type"].Value = Type;
                    newConfigNode.Attributes["moniker"].Value = Moniker;
                    configParentNode.AppendChild(newConfigNode);
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Adapter Provider Added to ESB Configuration!";
                }
                else
                {
                    existingConfigNode.Attributes["name"].Value = Name;
                    existingConfigNode.Attributes["type"].Value = Type;
                    existingConfigNode.Attributes["moniker"].Value = Moniker;
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Adapter Provider Updated in ESB Configuration!";
                }
            }
            else
            {
                return "Adapter Providers Xpath query returned null...ending process...";
            }
        }

        static private string SetItineraryService(string Id, string Name, string Type, string Scope, string Stage, string ServiceCategory)
        {
            WriteOutput(string.Format("Setting Itinerary Service with Id = {0}, Name = {1}, Type = {2}, Scope = {3}, Stage = {4}, Service Category = {5}..."
                , Id, Name, Type, Scope, Stage, ServiceCategory));

            var configParentNode = esbConfigXml.SelectSingleNode(XPATH_ItineraryServices);
            if (configParentNode != null)
            {
                WriteOutput("ITINERARY SERVICES = " + configParentNode.OuterXml);
                var existingConfigNode = configParentNode.SelectSingleNode(@"./itineraryService[@name='" + Name + "']");

                if (existingConfigNode == null)
                {
                    var newConfigNode = configParentNode.FirstChild.Clone();
                    newConfigNode.Attributes["name"].Value = Name;
                    newConfigNode.Attributes["type"].Value = Type;
                    newConfigNode.Attributes["id"].Value = Id;
                    newConfigNode.Attributes["scope"].Value = Scope;
                    newConfigNode.Attributes["stage"].Value = Stage;
                    if (newConfigNode.Attributes["serviceCategory"] != null)
                        newConfigNode.Attributes["serviceCategory"].Value = ServiceCategory;
                    configParentNode.AppendChild(newConfigNode);
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Itinerary Service Added to ESB Configuration!";
                }
                else
                {
                    existingConfigNode.Attributes["name"].Value = Name;
                    existingConfigNode.Attributes["type"].Value = Type;
                    existingConfigNode.Attributes["id"].Value = Id;
                    existingConfigNode.Attributes["scope"].Value = Scope;
                    existingConfigNode.Attributes["stage"].Value = Stage;
                    if (existingConfigNode.Attributes["serviceCategory"] != null)
                        existingConfigNode.Attributes["serviceCategory"].Value = ServiceCategory;
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Itinerary Service Updated in ESB Configuration!";
                }
            }
            else
            {
                return "Itinerary Services Xpath query returned null...ending process...";
            }
        }

        static private string SetFilter(string Name, string Type)
        {
            WriteOutput(string.Format("Setting Filter with Name = {0}, Type = {1}...", Name, Type));

            var configParentNode = esbConfigXml.SelectSingleNode(XPATH_Filters);
            if (configParentNode != null)
            {
                WriteOutput("FILTERS = " + configParentNode.OuterXml);
                var existingConfigNode = configParentNode.SelectSingleNode(@"./filter[@name='" + Name + "']");

                if (existingConfigNode == null)
                {
                    var newConfigNode = configParentNode.FirstChild.Clone();
                    newConfigNode.Attributes["name"].Value = Name;
                    newConfigNode.Attributes["type"].Value = Type;
                    configParentNode.AppendChild(newConfigNode);
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Fitler Added to ESB Configuration!";
                }
                else
                {
                    existingConfigNode.Attributes["name"].Value = Name;
                    existingConfigNode.Attributes["type"].Value = Type;
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Filter Updated in ESB Configuration!";
                }
            }
            else
            {
                return "Filters Xpath query returned null...ending process...";
            }
        }

        static private string SetItineraryOM(string Name, string Namespace, string Type)
        {
            WriteOutput(string.Format("Setting Itinerary Object Model with Name = {0}, Type = {1}, Namespace = {2}...", Name, Type, Namespace));

            var configParentNode = esbConfigXml.SelectSingleNode(XPATH_ItineraryOMs);
            if (configParentNode != null)
            {
                WriteOutput("ITINERARY OBJECT MODELS = " + configParentNode.OuterXml);
                var existingConfigNode = configParentNode.SelectSingleNode(@"./add[@name='" + Name + "']");

                if (existingConfigNode == null)
                {
                    var newConfigNode = configParentNode.FirstChild.Clone();
                    newConfigNode.Attributes["name"].Value = Name;
                    newConfigNode.Attributes["type"].Value = Type;
                    newConfigNode.Attributes["namespace"].Value = Namespace;
                    configParentNode.AppendChild(newConfigNode);
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Itinerary OM Added to ESB Configuration!";
                }
                else
                {
                    existingConfigNode.Attributes["name"].Value = Name;
                    existingConfigNode.Attributes["type"].Value = Type;
                    existingConfigNode.Attributes["namespace"].Value = Namespace;
                    esbConfigXml.Save(esbConfigFullPath);
                    return "Itinerary OM Updated in ESB Configuration!";
                }
            }
            else
            {
                return "Itinerary Ojbect Models Xpath query returned null...ending process...";
            }
        }

        #endregion

        #region Shared Functions

        static private void WriteOutput(string message)
        {
            //TODO: Add to log file
            Console.WriteLine(message);
        }

        static private System.Collections.Generic.Dictionary<string, string> GetParamters(string[] parameters)
        {
            if (parameters != null)
            {
                if (parameters.Length > 0)
                {
                    var paramDictionary = new Dictionary<string, string>();
                    foreach (var p in parameters)
                    {
                        if (p.Contains(":") && p.StartsWith("-"))
                        {
                            paramDictionary.Add(p.Substring(1, p.IndexOf(":") - 1).ToUpper(), p.Substring(p.IndexOf(":") + 1));
                        }
                        else
                        {
                            Console.WriteLine("Invalid Paramter: " + p);
                        }
                    }
                    return paramDictionary;
                }
                else
                {
                    return null;
                }
            }
            else
            {
                return null;
            }
        }

        static private string GetEsbConfigPath()
        {
            var esbInstallPathFromRegistry = (string)Microsoft.Win32.Registry.GetValue(REG_EsbToolkitInstallPath_FOLDER, REG_EsbToolkitInstallPath_KEY, "Not Found");
            WriteOutput("Updating esb.config located at " + esbInstallPathFromRegistry);
            WriteOutput("");
            return esbInstallPathFromRegistry;
        }

        static private void BackupEsbConfig()
        {

        }

        static private bool UpdateConfigurationFile(string FullFilePath, string NodeToUpdateXpath, string NewValue)
        {
            bool NodeUpdated = false;
            XmlTextReader reader = new XmlTextReader(FullFilePath);
            XmlDocument doc = new XmlDocument();
            doc.Load(reader);
            reader.Close();

            XmlElement root = doc.DocumentElement;
            var nodeToUpdate = root.SelectSingleNode(NodeToUpdateXpath);
            if (nodeToUpdate != null)
            {
                //save the output to a file
                if (nodeToUpdate.NodeType == XmlNodeType.Attribute)
                    nodeToUpdate.Value = NewValue;
                if (nodeToUpdate.NodeType == XmlNodeType.Element)
                    nodeToUpdate.InnerText = NewValue;

                doc.Save(FullFilePath);
                NodeUpdated = true;
            }
            return NodeUpdated;
        }

        #endregion
    }
}
